package com.minhang.aspect;

import com.alibaba.fastjson.JSON;

import com.minhang.annotation.log.SysLog;
import com.minhang.domain.Employee;
import com.minhang.domain.LogWithBLOBs;
import com.minhang.service.LogService;
import com.minhang.utils.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * @author rookie
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    @Autowired
    private LogService logService;

    @Pointcut("@annotation(com.minhang.annotation.log.SysLog)")
    public void logPointcut() {

    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        //获取调用的类名方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取自定义注解上的值
        SysLog sysLog = method.getAnnotation(SysLog.class);
        //执行时长
        currentTime.set(System.currentTimeMillis());
        result = joinPoint.proceed();
        Result newResult = (Result) result;
        LogWithBLOBs log = new LogWithBLOBs(newResult.getCode() == 200 ? "INFO" : "ERROR", System.currentTimeMillis() - currentTime.get());
        currentTime.remove();
        log.setExceptionDetail(newResult.getCode() != 200 ? newResult.getMessage() : null);
        //获取调用的类名方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.setUsername(getUsername());
        log.setCreateTime(new Date());
        log.setMethod(className + "." + methodName + "()");
        log.setDescription(sysLog.value());
        log.setParams(getParameter(method, joinPoint.getArgs()));
        logService.save(log);
        return result;
    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        //获取调用的类名方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取自定义注解上的值
        SysLog sysLog = method.getAnnotation(SysLog.class);
        LogWithBLOBs log = new LogWithBLOBs("ERROR", System.currentTimeMillis() - currentTime.get());
        currentTime.remove();
        log.setExceptionDetail(e.getMessage());
        //获取调用的类名方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.setUsername(getUsername());
        log.setCreateTime(new Date());
        log.setMethod(className + "." + methodName + "()");
        log.setDescription(sysLog.value());
        log.setParams(getParameter(method, joinPoint.getArgs()));
        logService.save(log);
    }

    /**
     * 获取操作人
     *
     * @return
     */
    public String getUsername() {
        try {
            return ((Employee) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getName();
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 获得参数
     *
     * @param method
     * @param args
     * @return
     */
    public String getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                if (args[i] != null) {
                    argList.add(args[i]);
                }
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                if (args[i] != null) {
                    Map<String, Object> map = new HashMap<>();
                    String key = parameters[i].getName();
                    if (!StringUtils.isEmpty(requestParam.value())) {
                        key = requestParam.value();
                    }
                    map.put(key, args[i]);
                    argList.add(map);
                }
            }
        }
        if (argList.size() == 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    argList.add((parameters[i].getName() + ":" + args[i].toString() + ","));
                }

            }
        }
        return argList.size() == 1 ? JSON.toJSONString(argList.get(0)) : JSON.toJSONString(argList);
    }
}
